<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2021/9/13
 * Time: 16:26
 */

namespace app\index\controller;


use think\Controller;
use think\facade\Request;
use think\facade\Db;
use think\facade\View;


use app\common\model\Users as UsersModel;
use app\common\model\Order as OrderModel;

use app\common\model\Message as MessageModel;
use app\common\model\AesUtil;

use PHPQRCode\QRcode;

/**
 * 微信支付V3
 * Class Pay
 * @package app\index\controller
 */
class Weixinpay extends Base
{
    protected $payApi = [
        'app' => 'https://api.mch.weixin.qq.com/v3/pay/transactions/app',//APP支付
        'h5'  => 'https://api.mch.weixin.qq.com/v3/pay/transactions/h5',//H5支付
        'jsapi' =>'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi',
        'native' =>'https://api.mch.weixin.qq.com/v3/pay/transactions/native',//扫码支付
    ];//支付接口列表
    protected $queryApi = 'https://api.mch.weixin.qq.com/v3/pay/transactions/id/{transaction_id}';//查询订单接口
    protected $refundApi = 'https://api.mch.weixin.qq.com/v3/refund/domestic/refunds';//退单接口
    protected $refundNotify = '******';//退单回调自定义

    protected $ord = [];

    public function _initialize()
    {

    }

    /**
     * 配置
     */
    public function config(){

        $webconfig = DB::name("webconfig")->where('vargroup','=',7)->select();
        $webconfig_arr = [];
        foreach($webconfig as $v){
            $webconfig_arr[$v['varname']] = $v['varvalue'];
        }

        return [
            'appid' => $webconfig_arr['cfg_wx_app_id'],
            'mchid' => $webconfig_arr['cfg_wx_mch_id'],//商户号
            'serial_no' => $webconfig_arr['cfg_serial_no'],//证书序列号
            'description' => '测试',//产品名称
            'notify' => $webconfig_arr['cfg_notify'],//支付回调
            'v3key'=>$webconfig_arr['cfg_v3key'],
            'apiclient_key'=>$webconfig_arr['cfg_apiclient_key'],//证书密钥
        ];

        /*return [
            'appid' => 'wxe5d55d481983958e',
            'mchid' => '1623717524',//商户号
            'serial_no' => '720F815160027F8E2B086AFEA1B3BA274ED7066D',//证书序列号
            'description' => '测试',//产品名称
            'notify' => 'http://hzsczx.com/index/weixinpay/notify',//支付回调
            'v3key'=>'TxU8fuusbJmQ3ZcLdZFS46ljasJ1Tang',
        ];*/
    }

    /**
     * 设置支付url
     * @param string $type  支付平台
     * @return mixed
     */
    public function getPayUrl($type = 'app'){
        return $this->payApi[$type];
    }

    //二维码显示页面
    public function gopay(){
        $param = input('param.');
        $type = isset($param['type'])?$param['type']:'pc';
        $url = $param['url'];
        $order_id = $param['order_id'];

        View::assign('type',$type);
        View::assign('url',$url);
        View::assign('order_id',$order_id);
        return View::fetch('index/gopay');
    }

    //支付状态检测
    public function pay_status_check(){
        $param = input("param.");
        $order_id = $param['order_id'];

        $OrderModel = new OrderModel();
        $res= $OrderModel->where("id",'=',$order_id)->find();

        return dyajaxReturn(0,'返回',$res['pay_status']);
    }


    public function pcPay(){
        $param = input('param.');
        $order_id = $param['order_id'];
        $OrderModel = new OrderModel();
        $ord = $OrderModel -> where('id','=',$order_id)->find();
        $this->ord = $ord;

        if(isMobile()){
            $res = $this->h5Pay();
            $arr['type'] = 'mobile';
            $arr['url'] = $res;

        }else{
            $res = $this->nativePay();
            $arr['type'] = 'pc';
            $arr['url'] = $res;
        }

        return dyajaxReturn(1,'返回',$arr);
    }


    /**
     * native
    */
    public function nativePay(){
        $ord = $this->ord;
        $order_sn = $ord['order_sn'];
        $total = $ord['price'] * 100;
        $title = $ord['title'];
        //请求参数(报文主体)
        $config = $this->config();
        $body = [
            'appid' => $config['appid'],
            'mchid' => $config['mchid'],
            'description' => $title,
            'out_trade_no' => $order_sn,
            'notify_url' => $config['notify'],
            'amount' => [
                'total' => $total,
                'currency' => 'CNY'
            ],
        ];

        $headers = $this->sign('POST',$this->getPayUrl('native'),json_encode($body));
        $res = $this->curl_post($this->getPayUrl('native'),json_encode($body),$headers);
        $res = json_decode($res,true);

        $code_url = $this->setQrcode($res['code_url']);
        //return dyajaxReturn(1,'h5链接',$code_url);
        return $code_url;
    }

    /**
     * H5支付
    */
    public function h5Pay(){
        $ord = $this->ord;
        $order_sn = $ord['order_sn'];
        $total = $ord['price'] * 100;
        $title = $ord['title'];

        //请求参数(报文主体)
        $config = $this->config();
        $body = [
            'appid' => $config['appid'],
            'mchid' => $config['mchid'],
            'description' => $title,
            'out_trade_no' => $order_sn,
            'notify_url' => $config['notify'],
            'amount' => [
                'total' => $total,
                'currency' => 'CNY'
            ],
            'scene_info' => [
                'payer_client_ip' => get_client_ip(),
                'h5_info' => [
                    'type' => 'Wap',
                ],
            ],
        ];
        $headers = $this->sign('POST',$this->getPayUrl('h5'),json_encode($body));
        $res = $this->curl_post($this->getPayUrl('h5'),json_encode($body),$headers);
        $res = json_decode($res,true);

        return $res['h5_url'];
    }




    /**
     * jsapi支付
     * @param $total  int  支付金额
     * @return mixed
     */
    public function jsapiPay($param){
        //请求参数(报文主体)
        $config = $this->config();

        $users_id = session("index_id");
        $UsersModel = new UsersModel();
        $user_one = $UsersModel -> where('id','=',$users_id)->find();

        $body = [
            'appid' => $config['appid'],
            'mchid' => $config['mchid'],
            'description' => $param['purpose'].$param['sn'],
            //'out_trade_no' => $this->getOrderNo(),
            'out_trade_no' => $param['sn'],
            'notify_url' => $config['notify'],
            'amount' => [
                'total' => 1,
//                'total' => $param['pay_price']*100,
                'currency' => 'CNY'
            ],
            'payer'=>[
                'openid'=>$user_one['openid']
            ],
//            'scene_info' => [
//                'payer_client_ip' => get_client_ip(),
//                'h5_info' => [
//                    'type' => 'Wap',
//                ],
//            ],
        ];
        $headers = $this->sign('POST',$this->getPayUrl('jsapi'),json_encode($body));
        return $this->curl_post($this->getPayUrl('jsapi'),json_encode($body),$headers);
    }

    /**
     * APP支付
     * @param $total  int  支付金额
     * @return mixed
     */
    public function appPay($total){
        //请求参数(报文主体)
        $config = $this->config();
        $body = [
            'appid' => $config['appid'],
            'mchid' => $config['mchid'],
            'description' => $config['description'],
            'out_trade_no' => $this->getOrderNo(),
            'notify_url' => $config['notify'],
            'amount' => [
                'total' => $total,
                'currency' => 'CNY'
            ],
        ];
        $headers = $this->sign('POST',$this->getPayUrl(),json_encode($body));
        return $this->curl_post($this->getPayUrl(),json_encode($body),$headers);
    }

    /**
     * 付钱调起支付js的回文
     */
    public function preOrder(){
        $param = input('param.');

        $res = $this->jsapiPay($param);
        $arr = json_decode($res,true);
        $prepay_id = $arr['prepay_id'];
        $package = "prepay_id=".$prepay_id;
        $timestamp = time();
        $nonce = $this->getRandomStr(32);//随机串
        $paySign = $this->paySign($prepay_id,$timestamp,$nonce);

        $data = [
            'timestamp'=>$timestamp,
            'nonceStr'=>$nonce,
            'package'=>$package,
            'paySign'=>$paySign,
            //'prepay_id'=>$arr['prepay_id'],
        ];

        return dyajaxReturn(1,2,$data);

        //print_r($paySign);die;
    }

    /**
     * 签名2 JSAPI调起支付API paySign
     */
    public function paySign($prepay_id,$timestamp,$nonce){
        //请求参数(报文主体)
        $config = $this->config();

        $mch_private_key = $this->getMchKey();//私钥
        //$timestamp = time();//时间戳
        //$nonce = $this->getRandomStr(32);//随机串
        //$body = "prepay_id=".$prepay_id;
        //构造签名串
//        $message =  $config['appid']."\n".
//            $timestamp."\n".
//            $nonce."\n".
//            $package."\n";//报文主体
        $message = join("\n", [$config['appid'], $timestamp, $nonce, "prepay_id={$prepay_id}", '']);
        //计算签名值
        openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
        $sign = base64_encode($raw_sign);

        return $sign;
    }

    /**
     * 支付回调
     */
    public function notify()
    {
        $config = $this->config();
//        $mchkey = 'FFyjbXJQtL5XmZsY7dgUkp8B7bHdtqM6'; //API密钥
        $mchkey = $config['v3key']; //API密钥
        //获得内容
        $post = file_get_contents("php://input");
        //转数组
        $arr_post = json_decode($post,true);
        //解密
        $ciphertext=(new AesUtil($mchkey))->decryptToString($arr_post['resource']['associated_data'],$arr_post['resource']['nonce'],$arr_post['resource']['ciphertext']);
        //转数组
        $arr_ciphertext = json_decode($ciphertext,true);

        if(!empty($arr_ciphertext['out_trade_no']) and isset($arr_ciphertext['trade_state']) and $arr_ciphertext['trade_state']=='SUCCESS')
        {

            Db::startTrans();
            try {
                $up_res = Db::name('order')->where('order_sn','=',$arr_ciphertext['out_trade_no'])->update([
                    'transaction_id'=>$arr_ciphertext['transaction_id'],
                    'pay_status'=>3,
                    'pay_time'=>time(),
                ]);
                if(!$up_res){
                    Db::rollback();
                    //return dyajaxReturn(0,'订单记录失败');
                    $arr = array("code" => "ERROR", "message" => '订单记录失败');
                    echo json_encode($arr);
                }
                /*$loaned_one = Db::name('loaned')->where('sn','=',$arr_ciphertext['out_trade_no'])->find();
                //$users_id = session("index_id");
                if($loaned_one['type'] == 1){
                    $users_id = $loaned_one['jie_users_id'];
                }
                if($loaned_one['type'] == 2){
                    $users_id = $loaned_one['chu_users_id'];
                }
                $MessageModel = new MessageModel();
                $res = $MessageModel -> add(1,$loaned_one['loaned_id'],$users_id,$loaned_one);

                if($res['code'] == 0){
                    Db::rollback();
                    //return dyajaxReturn(0,'消息记录失败');
                    $arr = array("code" => "ERROR", "message" => '消息记录失败');
                    echo json_encode($arr);
                }*/
                Db::commit();
                $arr = array("code" => "SUCCESS", "message" => "成功回调");
                echo json_encode($arr);
                //return dyajaxReturn(1,'成功回调');
            } catch (\Exception $e) {
                Db::rollback();
                //$result['msg'] = json($e->getMessage());
                //return dyajaxReturn(0,$e->getMessage());
                $arr = array("code" => "ERROR", "message" => $e->getMessage());
                echo json_encode($arr);
            }
            //成功回调
            //var_dump($arr_ciphertext);
        }
    }

    /**
     * 退款
     * @param string $transaction_id  平台订单号
     * @return mixed
     */
    public function refund($transaction_id = ''){
        $body = [
            'transaction_id' => $transaction_id,//平台订单号
            'out_refund_no' => $this->getOrderNo(),//系统退款单号
            'reason' => '退款',//退款原因
            'notify_url' => '',//退款回调
            'amount' => [
                'refund' => 1,//退款金额
                'total' => 1,//原订单金额
                'currency' => 'CNY',
            ],
        ];
        $headers = $this->sign('POST',$this->refundApi,json_encode($body));
        return $this->curl_post($this->refundApi,json_encode($body),$headers);
    }

	/**
     * 退款回调
     */
    public function refundNotify(){

    }
    
    /**
     * 签名
     * @param string $http_method    请求方式GET|POST
     * @param string $url            url
     * @param string $body           报文主体
     * @return array
     */
    public function sign($http_method = 'POST',$url = '',$body = ''){
        $mch_private_key = $this->getMchKey();//私钥
        //dump($mch_private_key);
        $timestamp = time();//时间戳
        $nonce = $this->getRandomStr(32);//随机串
        $url_parts = parse_url($url);
        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
        //构造签名串
        $message = $http_method."\n".
            $canonical_url."\n".
            $timestamp."\n".
            $nonce."\n".
            $body."\n";//报文主体
        //计算签名值
        openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
        $sign = base64_encode($raw_sign);
        //设置HTTP头
        $config = $this->config();
        $token = sprintf('WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            $config['mchid'], $nonce, $timestamp, $config['serial_no'], $sign);
        $headers = [
            'Accept: application/json',
            'User-Agent: */*',
            'Content-Type: application/json; charset=utf-8',
            'Authorization: '.$token,
        ];
        return $headers;
    }



    //私钥
    public function getMchKey(){
        //path->私钥文件存放路径
//        return openssl_get_privatekey(file_get_contents('path'));
        //return openssl_get_privatekey(file_get_contents(public_path().'/static/apiclient_key.pem'));
        $config = $this->config();
        $apiclient_key = $config['apiclient_key'];
        return openssl_get_privatekey($apiclient_key);
    }

    //post请求
    public function curl_post($url , $data,$headers=array())
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        //设置header头
        curl_setopt($ch, CURLOPT_HTTPHEADER,$headers);
        // POST数据
        curl_setopt($ch, CURLOPT_POST, 1);
        // 把post的变量加上
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        $output = curl_exec($ch);
        curl_close($ch);
        return $output;
    }

    //get请求
    public function curl_get($url,$headers=array())
    {
        $info = curl_init();
        curl_setopt($info,CURLOPT_RETURNTRANSFER,true);
        curl_setopt($info,CURLOPT_HEADER,0);
        curl_setopt($info,CURLOPT_NOBODY,0);
        curl_setopt($info,CURLOPT_SSL_VERIFYPEER,false);
        curl_setopt($info,CURLOPT_SSL_VERIFYPEER,false);
        curl_setopt($info,CURLOPT_SSL_VERIFYHOST,false);
        //设置header头
        curl_setopt($info, CURLOPT_HTTPHEADER,$headers);
        curl_setopt($info,CURLOPT_URL,$url);
        $output = curl_exec($info);
        curl_close($info);
        return $output;
    }

    /**
     * 获得随机字符串
     * @param $len      integer       需要的长度
     * @param $special  bool      是否需要特殊符号
     * @return string       返回随机字符串
     */
    public function getRandomStr($len, $special=false){
        $chars = array(
            "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
            "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
            "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G",
            "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
            "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2",
            "3", "4", "5", "6", "7", "8", "9"
        );

        if($special){
            $chars = array_merge($chars, array(
                "!", "@", "#", "$", "?", "|", "{", "/", ":", ";",
                "%", "^", "&", "*", "(", ")", "-", "_", "[", "]",
                "}", "<", ">", "~", "+", "=", ",", "."
            ));
        }

        $charsLen = count($chars) - 1;
        shuffle($chars);                            //打乱数组顺序
        $str = '';
        for($i=0; $i<$len; $i++){
            $str .= $chars[mt_rand(0, $charsLen)];    //随机取出一位
        }
        return $str;
    }

    /**
     * 生成订单号
     */
    public function getOrderNo(){
        return date('Ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
    }


    public function setQrcode($url){
        //二维码图片保存路径
        $path = 'qrcode/'.date("Ymd");
        $pathname = public_path().$path;
        if(!is_dir($pathname)) { //若目录不存在则创建之
            mkdir($pathname,0777,true);
        }
        $qrcode = new QRcode();
        //二维码图片保存路径(若不生成文件则设置为false)
        $suijishu = date("YmdHis").rand(100,999);
        $filename = $pathname . "/qrcode_" . $suijishu . ".png";//生成路径
        $destName = $path."/qrcode_" . $suijishu . ".png";//返回路径
        //二维码容错率，默认L
        $level = "L";
        //二维码图片每个黑点的像素，默认4
        $size = '10';
        //二维码边框的间距，默认2
        $padding = 2;
        //保存二维码图片并显示出来，$filename必须传递文件路径
        $saveandprint = true;
        //生成二维码图片
        $qrcode->png($url,$filename,$level,$size,$padding,$saveandprint);
        //二维logo
        //$logo = "./public/wxpay/images/logo.png";
        //$QR = imagecreatefromstring(file_get_contents($filename));
        //$logo = imagecreatefromstring(file_get_contents($logo));
        //$QR_width = imagesx($QR);
        //$QR_height = imagesy($QR);
        //$logo_width = imagesx($logo);
        //$logo_height = imagesy($logo);
        //$logo_qr_width = $QR_width / 5;
        //$scale = $logo_width / $logo_qr_width;
        //$logo_qr_height = $logo_height / $scale;
        //$from_width = ($QR_width - $logo_qr_width) / 2;
        //imagecopyresampled($QR, $logo, $from_width, $from_width, 0, 0, $logo_qr_width, $logo_qr_height, $logo_width, $logo_height);
        //imagecopyresampled($QR, $logo, $from_width, $from_width, 0, 0, $logo_qr_width, $logo_qr_height, $logo_width, $logo_height);
        //imagepng($QR,$filename);
        return $destName;
    }

}